﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Rubik.Base
{
    /// <summary>
    /// represents binary string builder.
    /// </summary>
    public class BinaryString
    {
        private readonly StringBuilder builder;

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryString"/> class.
        /// </summary>
        public BinaryString()
        {
            builder = new StringBuilder();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryString"/> class.
        /// </summary>
        /// <param name="binaryString">The binary string.</param>
        public BinaryString(string binaryString)
        {
            builder = new StringBuilder(binaryString);
        }

        /// <summary>
        /// Pushes the zero value.
        /// </summary>
        public void PushZero()
        {
            builder.Append("0");
        }

        /// <summary>
        /// Pushes the 1 value.
        /// </summary>
        public void PushOne()
        {
            builder.Append("1");
        }

        /// <summary>
        /// Pushes the specified Int32 value.
        /// </summary>
        /// <param name="value">The value.</param>
        public void Push(int value)
        {
            Push(value, 32);
        }

        /// <summary>
        /// Pushes the specified Int32 value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public void Push(int value, int numberOfBits)
        {
            if (numberOfBits < 1 || numberOfBits > 32)
            {
                throw new ArgumentOutOfRangeException("numberOfBits",
                    "Invalid number of bits to read from the value");
            }
            Stack<string> stack = new Stack<string>();
            for (int i = 0; i < numberOfBits; i++)
            {
                if ((value & 1) == 1)
                {
                    stack.Push("1");
                }
                else
                {
                    stack.Push("0");
                }
                value >>= 1;
            }
            while (stack.Count > 0)
            {
                builder.Append(stack.Pop());
            }
        }

        /// <summary>
        /// Pops the bit.
        /// </summary>
        /// <param name="numberOfBits">The number of bits.</param>
        /// <returns>
        /// The bit value. 0 or 1. -1 if the end of the string is reached.
        /// </returns>
        public int PopBits(int numberOfBits)
        {
            if (numberOfBits < 1 || numberOfBits > 32)
            {
                throw new ArgumentOutOfRangeException("numberOfBits",
                    "Invalid number of bits to read from the binary string");
            }
            if (builder.Length == 0)
            {
                return -1;
            }
            int result = 0;
            for (int i = 0; i < numberOfBits; i++)
            {
                if (builder.Length == 0)
                {
                    break;
                }
                result <<= 1;
                int bitValue = Int32.Parse(builder[0].ToString());
                result |= bitValue;
                builder.Remove(0, 1);
            }
            return result;
        }

        public string Build()
        {
            return builder.ToString();
        }
    }
}
